/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "MyTest"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <thread>

#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>

#include <gui/BufferQueue.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <ui/FenceTime.h>
#include <ui/GraphicBuffer.h>

#include "FakeCameraConsumer.h"

using namespace android;

// Macros for including the GLConsumer name in log messages
#define LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
//#define LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)

//////////////////////////////////////////////////////////


#if 1

sp<FakeCameraConsumer> fakeCameraConsumer;

static void consumer_process_thread()
{
    #define AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 0x23
    int format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
    int width = 1920;
    int height = 1080;

    uint32_t frame_size = width * height * 1.5;

    FILE *fp = fopen("/data/local/tmp/1080p_nv12_consumer.yuv", "w+");
    if (fp == NULL)
    {
        printf("fopen for write failed!\n");
    }

    while (1)
    {
        BufferItem item;
        status_t status = fakeCameraConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0), 0);
        if (status == NO_ERROR)
        {
            sp<GraphicBuffer> graphicBuffer;// = fakeCameraConsumer->mSlots[item.mSlot].mGraphicBuffer;
            fakeCameraConsumer->getGraphicBuffer(item.mSlot, graphicBuffer);
            
            uint32_t* dataOut;
            graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&dataOut));

            //error: item.mFrameNumber is uint64_t type
            //printf("slot=%d, frameNum=%d, the data out is %d, dataOut=0x%x\n", item.mSlot, item.mFrameNumber, *dataOut, dataOut);

            printf("slot=%d, frameNum=%llu, the data out is %d, dataOut=0x%x\n", item.mSlot, item.mFrameNumber, *dataOut, dataOut);
            //printf("slot=%d, frameNum=%d, the data out is %d, dataOut=0x%x\n", item.mSlot, (int)item.mFrameNumber, *dataOut, dataOut);

            if (fp)
            {
                fwrite(dataOut, 1, frame_size, fp);
                //fflush(fp);
            }

            graphicBuffer->unlock();

            fakeCameraConsumer->releaseBuffer(item.mSlot);
        }

        usleep(1000000);
    }

    if (fp)
        fclose(fp);
}

int main(int argc, char** argv)
{
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);

    fakeCameraConsumer = new FakeCameraConsumer(consumer, false);

    sp<IServiceManager> serviceManager = defaultServiceManager();
    serviceManager->addService(String16("MyProducer"), IInterface::asBinder(producer));
    serviceManager->addService(String16("MyConsumer"), IInterface::asBinder(consumer));

    ProcessState::self()->startThreadPool();

    std::thread consumer_th(consumer_process_thread);

    IPCThreadState::self()->joinThreadPool();

    return 0;
}

#else

struct DummyConsumer : public BnConsumerListener {
    void onDisconnect() override
    {
        printf("producer is disconnected.\n");
    }

    void onFrameAvailable(const BufferItem& item) override 
    {
        printf("onFrameAvailable: framenumber=%llu.\n", item.mFrameNumber);
    }

    void onBuffersReleased() override 
    {

    }

    void onSidebandStreamChanged() override 
    {

    }

};

// void consumer_process_thread(void *arg)
// {
//     sp<IGraphicBufferConsumer> consumer = ;
// }

sp<IGraphicBufferConsumer> consumer;

void consumer_process_thread()
{
    /****************** Consumer ***********************/
    while (1)
    {
        //printf("#####################\n");

        BufferItem item;
        status_t status = consumer->acquireBuffer(&item, static_cast<nsecs_t>(0));
        if (status == NO_ERROR)
        {
            if (item.mGraphicBuffer != NULL)
            {
                //printf("##################1##\n");
                //if (item.mGraphicBuffer == NULL)
                //    printf("##################1#mGraphicBuffer=NULL#\n");
                //printf("##################1#item=0x%x#\n",item);
                //printf("##################1#item.mGraphicBuffer=0x%x#\n", reinterpret_cast<uint32_t *>(item.mGraphicBuffer));
                //printf("##################1##\n");
                uint32_t* dataOut;
                item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
                                reinterpret_cast<void**>(&dataOut));
                printf("the data out is %d\n", *dataOut);
                item.mGraphicBuffer->unlock();
                //printf("###############2######\n");                   
            }
            else
            {
                printf("item.mAcquireCalled=%d, slot=%d, item.mFrameNumber=%d\n", item.mAcquireCalled, item.mSlot, item.mFrameNumber);
            }

            printf("server get slot=%d, item.mFrameNumber=%d\n", item.mSlot, item.mFrameNumber);
        }

        consumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY,
            EGL_NO_SYNC_KHR, Fence::NO_FENCE);


        usleep(1000000);
    }
}

int main(int argc, char** argv)
{
    sp<IGraphicBufferProducer> producer;
    //sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);

    consumer->consumerConnect(new DummyConsumer, false);

    sp<IServiceManager> serviceManager = defaultServiceManager();
    serviceManager->addService(String16("MyProducer"), IInterface::asBinder(producer));
    serviceManager->addService(String16("MyConsumer"), IInterface::asBinder(consumer));

    ProcessState::self()->startThreadPool();


    //std::thread consumer_th(consumer_process_thread, (void *)consumer);
    std::thread consumer_th(consumer_process_thread);

    IPCThreadState::self()->joinThreadPool();

    return 0;
}

#endif